/**
 * @author Tobias Boehm
 *
 * 14.04.2011
 */
package org.anddev.andengine.extension.tbsengine.match.playerslot;

import java.util.Collections;
import java.util.List;
import java.util.Vector;

import org.anddev.andengine.extension.tbsengine.faction.AbsBaseFaction;
import org.anddev.andengine.extension.tbsengine.menu.activity.AbsPrepareMatchActivity;
import org.anddev.andengine.extension.tbsengine.menu.view.AbsPrepareMatchChoiceDialog;
import org.anddev.andengine.extension.tbsengine.player.BasePlayer;
import org.anddev.andengine.extension.tbsengine.shared.IImageOrTextSelectableItem;
import org.anddev.andengine.extension.tbsengine.team.Team;


/**
 * AbsClientPlayerSlot represents an abstract player slot on the client side of 
 * your application.
 * <p>
 * This class extends {@link AbsPlayerSlot} with some methods for calling
 * "change team/faction/state" events of the <code>PrepareMatchActivityClass</code>
 * you have to define by implementing this class and "set team/faction/state" 
 * methods for transporting information about such changes to the <code>PlayerSlotChangedListenerClass</code>
 * which implements {@link IAbsPlayerSlotChangedListener}.
 * <p>
 * It also contains some methods for querying lists of possible choices for 
 * team/faction/state changes. You can use those methods for your implementations 
 * of {@link AbsPrepareMatchChoiceDialog}s.
 *   
 * @author Tobias Boehm
 *
 * @param <FactionBaseClass> Your base class for all your factions which extends {@link AbsBaseFaction}.
 * @param <PlayerSlotChangedListenerClass> A class (most likely a view) which implements {@link IAbsPlayerSlotChangedListener}.
 * @param <PrepareMatchActivityClass> Your PrepareMatchActivity which extends {@link AbsPrepareMatchActivity}.
 */
public abstract class AbsClientPlayerSlot<FactionBaseClass extends AbsBaseFaction,
										  PlayerSlotChangedListenerClass extends IAbsPlayerSlotChangedListener<FactionBaseClass>,
										  PrepareMatchActivityClass extends AbsPrepareMatchActivity<?,FactionBaseClass,?,?>>

										  extends AbsPlayerSlot<FactionBaseClass>{
	
	
	/**
	 * A reference to an object its class implements or/and extends {@link IAbsPlayerSlotChangedListener}.
	 */
	protected PlayerSlotChangedListenerClass mPlayerSlotChangedListener;
	
	/**
	 * A reference to an objects its class extends
	 * {@link AbsPrepareMatchActivity}. 
	 */
	protected final PrepareMatchActivityClass mPrepareMatchActivity;

	/**
	 * Use this constructor to create a player slot which is occupied by a player.
	 * 
	 * @param pSlotID
	 * @param pPrepareMatchActivity
	 * @param pPlayer The occupying player.
	 * @param pTeam
	 * @param pFaction
	 */
	public AbsClientPlayerSlot(final long pSlotID, final PrepareMatchActivityClass pPrepareMatchActivity, final BasePlayer pPlayer, final Team pTeam, final FactionBaseClass pFaction){
		super(pSlotID, pPlayer, pTeam, pFaction);
		
		this.mPrepareMatchActivity = pPrepareMatchActivity;
	}
	
	/**
	 * Use this constructor to create a player slot which is open or closed and 
	 * does not include a player yet.
	 * 
	 * @param pSlotID
	 * @param pPrepareMatchActivity
	 * @param pTeam
	 * @param pFaction
	 * @param pSlotState The slot state (has to be <code>SlotState.OPEN</code> or <code>SlotState.CLOSED</code>
	 */
	public AbsClientPlayerSlot(final long pSlotID, final PrepareMatchActivityClass pPrepareMatchActivity, final Team pTeam, final FactionBaseClass pFaction, final SlotState pSlotState){
		super(pSlotID, pTeam, pFaction, pSlotState);
		
		this.mPrepareMatchActivity = pPrepareMatchActivity;
	}
	
	/**
	 * This method should return a list of containing players and slot states 
	 * (for example  <code>SlotState.OPEN</code> or <code>SlotState.CLOSED</code>)
	 * its classes implements {@link IImageOrTextSelectableItem}.
	 * You may query those information from its <code>mPrepareMatchActivity</code>. 
	 * 
	 * @return A list of players and slot states.
	 */
	public abstract List<IImageOrTextSelectableItem> getSelectablePlayersAndSlotStates();
	
	/**
	 * This methods returns a vector of selectable teams sorted by their team 
	 * ID's.
	 * 
	 * @return A vector of selectable teams.
	 */
	public Vector<Team> getSelectableTeams(){
		
		final Vector<Team> teams = new Vector<Team>();
		
		teams.addAll(this.mPrepareMatchActivity.getTeams());
		
		Collections.sort(teams);
		
		return teams;
	}
	
	/**
	 * This method return a vector of selectable factions which extends <code>FactionBaseClass</code>.
	 * 
	 * @return A vector of selectable factions which extends <code>FactionBaseClass</code>.
	 */
	public Vector<FactionBaseClass> getSelectbaleFactions(){
		
		final Vector<FactionBaseClass> factions = new Vector<FactionBaseClass>();
		
		factions.addAll(this.mPrepareMatchActivity.getFactions());
		
		return factions;
	}
	
	/**
	 * This method sets the new player and notifies the
	 * <code>mPlayerSlotChangedListener</code> if its registered before.
	 * 
	 * @see #registerPlayerSlotChangedListener(IAbsPlayerSlotChangedListener)
	 * 
	 * @param pNewPlayer
	 */
	@Override
	public void setPlayer(final BasePlayer pNewPlayer) {
		super.setPlayer(pNewPlayer);
		
		if(this.hasPlayerSlotChangedListener()){
			
			this.mPrepareMatchActivity.runOnUiThread(new Runnable() {
				
				@Override
				public void run() {
					mPlayerSlotChangedListener.onPlayerChanged(pNewPlayer);	
				}
			});			
		}
	}
	
	/**
	 * This method should be called by a controller which is connected to a 
	 * (most likely visual) representation of the "choose a player" component
	 * which most likely extends {@link AbsPrepareMatchChoiceDialog}. 
	 * 
	 * @see AbsPrepareMatchChoiceDialog
	 * 
	 * @param pNewPlayer
	 */
	public void requestSlotPlayerChange(final BasePlayer pNewPlayer){
		
		this.mPrepareMatchActivity.sendRequestSlotPlayerChangeMessage(this.mSlotID, pNewPlayer);
	}
	
	
	
	/**
	 * This method sets the new slot state and notifies the
	 * <code>mPlayerSlotChangedListener</code> if its
	 * registered before and <code>pNewPlayerSlotState</code>
	 * has the value <code>SlotState.OPEN</code> or <code>SlotState.CLOSED</code>.
	 * 
	 * @see #registerPlayerSlotChangedListener(IAbsPlayerSlotChangedListener)
	 * 
	 * @param pNewPlayerSlotState
	 */
	@Override
	public void setSlotState(final SlotState pNewPlayerSlotState) {
				
		this.mPlayerSlotState = pNewPlayerSlotState;
		
		if((pNewPlayerSlotState == SlotState.OPEN) || (pNewPlayerSlotState == SlotState.CLOSED)){
					
			this.mPlayer = null;
			
			if(this.hasPlayerSlotChangedListener()){
				
				this.mPrepareMatchActivity.runOnUiThread(new Runnable() {
					
					@Override
					public void run() {
						mPlayerSlotChangedListener.onSlotOpenedOrClosed(pNewPlayerSlotState);
					}
				});
			}
		}
	}
		
	/**
	 * This method should be called by a controller which is connected to a 
	 * (most likely visual) representation of the "choose a slot state" component 
	 * which most likely extends {@link AbsPrepareMatchChoiceDialog}. 
	 * 
	 * @see AbsPrepareMatchChoiceDialog
	 * 
	 * @param pNewSlotState The new slot state which has to be <code>SlotState.OPEN</code> or <code>SlotState.CLOSED</code>.
	 */
	public void requestSlotToOpenOrClose(final SlotState pNewSlotState){
		
		if((pNewSlotState != SlotState.OPEN) && (pNewSlotState != SlotState.CLOSED)){
			
			throw new IllegalArgumentException();
		
		}else{
			
			this.mPrepareMatchActivity.sendRequestSlotToOpenOrCloseMessage(this.mSlotID, pNewSlotState);
		}
	}
	
	/**
	 * This method sets the new team and notifies the
	 * <code>mPlayerSlotChangedListener</code> if its registered before.
	 * 
	 * @see #registerPlayerSlotChangedListener(IAbsPlayerSlotChangedListener)
	 * 
	 * @param pNewTeam
	 */
	@Override
	public void setTeam(final Team pNewTeam) {
		super.setTeam(pNewTeam);
		
		if(this.hasPlayerSlotChangedListener()){
			
			this.mPrepareMatchActivity.runOnUiThread(new Runnable() {
				
				@Override
				public void run() {
					mPlayerSlotChangedListener.onTeamChangedListener(pNewTeam);
				}
			});
		}
	}
	
	/**
	 * This method should be called by a controller which is connected to a 
	 * (most likely visual) representation of the "choose a team" component 
	 * which most likely extends {@link AbsPrepareMatchChoiceDialog}. 
	 * 
	 * @see AbsPrepareMatchChoiceDialog 
	 * 
	 * @param pNewTeam
	 */
	public void requestSlotTeamChange(final Team pNewTeam){
		
		this.mPrepareMatchActivity.sendRequestSlotTeamChangeMessage(this.mSlotID, pNewTeam);
	}
		
	
	/**
	 * This method sets the new faction and notifies the <code>mPlayerSlotChangedListener</code> if its
	 * registered before.
	 * 
	 * @see #registerPlayerSlotChangedListener(IAbsPlayerSlotChangedListener)
	 * 
	 * @param pNewFaction
	 */
	@Override
	public void setFaction(final FactionBaseClass pNewFaction) {
		
		super.setFaction(pNewFaction);
		
		if(this.hasPlayerSlotChangedListener()){
			
			this.mPrepareMatchActivity.runOnUiThread(new Runnable() {
				
				@Override
				public void run() {
					mPlayerSlotChangedListener.onFactionChanged(pNewFaction);
				}
			});
		}
	};

	/**
	 * This method should be called by a controller which is connected to a 
	 * (most likely visual) representation of the "choose a faction" component 
	 * which most likely extends {@link AbsPrepareMatchChoiceDialog}. 
	 * 
	 * @see AbsPrepareMatchChoiceDialog 
	 * 
	 * @param pNewTeam
	 */
	public void requestSlotFactionChange(final FactionBaseClass pNewFaction){
		
		this.mPrepareMatchActivity.sendRequestSlotFactionChangeMessage(this.mSlotID, pNewFaction);
	}
		
	/**
	 * With this method registered a new
	 * {@link #mPlayerSlotChangedListener}.
	 * 
	 * Warning:
	 * If you call this method again it drops the old listener.
	 * 
	 * @param pPlayerSlotChangedListener
	 */
	public void registerPlayerSlotChangedListener(final PlayerSlotChangedListenerClass pPlayerSlotChangedListener) {
		this.mPlayerSlotChangedListener = pPlayerSlotChangedListener;
	}

	/**
	 * This method checks whether this object has a PlayerSlotChangedListener or 
	 * not.
	 * 
	 * @return 
	 */
	public boolean hasPlayerSlotChangedListener(){
		
		return (this.mPlayerSlotChangedListener != null);
	}
}
